﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO.Ports;
using System.IO;


namespace PipePrinter
{
    public enum linkMsgs
    {
        LINK_UP,
        LINK_DOWN,
        BYTES_RECEIVED,
        BYTES_SENT
    };

    public enum linkType
    { 
        NONE,
        INPUT_LINK,
        OUTPUT_LINK,
    }

    public class LinkLayer
    {

        public SerialPort sPort1;

        public delegate void eventCallBack(linkType type, linkMsgs message);
        public eventCallBack evCB;

        public delegate void bufferCallBack(byte data);
        public bufferCallBack bufCB;

        public TraceHelper Tracer;

#if DEBUG
        private FileStream fsDBInput;
        private FileStream fsDBOutput;
#endif
        public linkType LinkType = linkType.NONE;


        public void SerialPortSetup(string port, string baud, Parity parity, Handshake handShake, string dataBits, StopBits stop)
        {
            sPort1.PortName = port;
            sPort1.BaudRate = int.Parse(baud);
            sPort1.Handshake = handShake;
            sPort1.Parity = parity;
            sPort1.DataBits = int.Parse(dataBits);
            sPort1.StopBits = stop;

        }

        public void SetTraceCallback(traceCallBack trCB)
        {
            Tracer.SetTraceCallback(new traceCallBack(trCB));
        }

        public bool IsOpen()
        {
            return sPort1.IsOpen;
        }


        public void SetEventCallback(eventCallBack evt)
        {
            evCB = new eventCallBack(evt);
        }

        public void SetBufferCallback(bufferCallBack bvt)
        {
            bufCB = new bufferCallBack(bvt);
        }

        internal void sPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            int data;
            //call the receive callback
            int test = sPort1.BytesToRead;
            bool loop = sPort1.IsOpen;
            try
            {
                // drain the serial port buffer and 

                while (loop)
                {
                    if (sPort1.IsOpen == false)
                    {
                        loop = false;
                        continue;
                    }
                    if (sPort1.BytesToRead == 0)
                    {
                        loop = false;
                        continue;
                    }
                    // change this to read all bytes from the buffer
                    try
                    {
                        data = sPort1.ReadByte();
                    }
                    catch (Exception ex)
                    {
                        Tracer.traceException(ex);
                        continue;
                    }

                    if (data >= 0)
                    {
                        bufCB((byte)data);
#if DEBUG
                        //logByte((byte)data);
                        //Tracer.traceLog(data.ToString());
                        fsDBInput.WriteByte((byte)data);
                        fsDBInput.Flush();
#endif
                    }
                    else
                    {
                        loop = false;
                    }

                }
            }
            catch (Exception ex)
            {
                Tracer.traceException(ex);
            }
            evCB(this.LinkType, linkMsgs.BYTES_RECEIVED);
        }

        public LinkLayer()
        {
            sPort1 = new SerialPort();
            sPort1.WriteTimeout = 5000;

            Tracer = new TraceHelper();

            sPort1.DataReceived += new SerialDataReceivedEventHandler(sPort1_DataReceived);
        }

        public void openLink()
        {

            try
            {
                if (!sPort1.IsOpen)
                {
                    sPort1.Open();
                    evCB(this.LinkType, linkMsgs.LINK_UP);
#if DEBUG
                    fsDBInput = new FileStream(this.LinkType.ToString() + "_INPUT.bin", FileMode.Create);
                    fsDBOutput = new FileStream(this.LinkType.ToString() + "_OUTPUT.bin", FileMode.Create);
#endif


                }
            }
            catch (Exception ex)
            {
                // TODO - fix this SHCPBase should own this class now
                //MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                Tracer.traceException(ex);
                evCB(this.LinkType, linkMsgs.LINK_DOWN);
            }
        }

        public void closeLink()
        {
            sPort1.Close();
            evCB(this.LinkType, linkMsgs.LINK_DOWN);
#if DEBUG
            fsDBInput.Flush();
            fsDBInput.Close();
            fsDBOutput.Flush();
            fsDBOutput.Close();
#endif
        }

        public void write(byte[] buffer, int offset, int count)
        {
            sPort1.Write(buffer, offset, count);
#if DEBUG
            fsDBOutput.Write(buffer, offset, count);
            fsDBOutput.Flush();
#endif

            evCB(this.LinkType, linkMsgs.BYTES_SENT);
        }

        public void OnBaudRateChange(int val)
        {
            sPort1.BaudRate = val;
        }

        public void OnFlowControlChanged(Handshake val)
        {

            sPort1.Handshake = val;
        }

        public void OnParityChanged(Parity val)
        {
            sPort1.Parity = val;

        }

        public void OnDataBitsChanged(int val)
        {
            sPort1.DataBits = val;


        }

        public void OnStopBitsChanged(StopBits val)
        {
            sPort1.StopBits = val;
        }

        public void OnComPortChanged(string s)
        {
            sPort1.PortName = s;
        }
    }

    public class InputLink : LinkLayer
    {
        private static InputLink instance = null;

        public static InputLink getInstance()
        {
            if (instance == null)
            {
                instance = new InputLink();
                instance.LinkType = linkType.INPUT_LINK;
            }
            return instance;
        }
    }

    public class OutputLink : LinkLayer
    {
        private static OutputLink instance = null;

        public static OutputLink getInstance()
        {
            if (instance == null)
            {
                instance = new OutputLink();
                instance.LinkType = linkType.OUTPUT_LINK;
            }
            return instance;
        }
    }
}
